package com.yyy.modules.core;

import cn.hutool.core.util.ObjectUtil;
import com.yyy.common.Interceptors.IMsgCallbackHandler;
import com.yyy.common.annotation.CallbackHandler;
import com.yyy.common.annotation.MsgListener;
import com.yyy.common.exception.NoQueueNameSpecifiedException;
import com.yyy.entity.Message;
import com.yyy.entity.MessageHandler;
import com.yyy.entity.QueueWrapper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;


/**
 * @author yyy
 * @Date 2020-10-18 11:54
 * @Email yangyouyuhd@163.com
 */
@Data
@Slf4j
public class MsgFactory implements BeanPostProcessor {
    /**
     * 维护队列名和多个消费者的映射关系
     * 多个消费者监听同一队列时、默认采用轮询算法
     */
    private Map<String, ArrayList<MessageHandler>> msgHandlerMap;
    /**
     * 保存所有队列名
     */
    private List<String> queueNameList;

    /**
     * 队列名到队列的映射
     */
    private Map<String, QueueWrapper> mqMapping;

    /**
     * 如果下次收到消息事件、该翻拍哪个消费者的牌子呢、轮询
     */
    private Map<String, Integer> polling;

    /**
     * 如果用户为某个队列注入了回调处理类、则在此维护它
     */
    private Map<String,IMsgCallbackHandler> callbackHandlerMap;


    @Resource
    private ThreadPoolExecutor executor;

    public MsgFactory() {
        msgHandlerMap = new HashMap<>();
        queueNameList = new ArrayList<>();
        mqMapping = new HashMap<>();
        this.polling = new HashMap<>();
        this.callbackHandlerMap = new HashMap<>();
    }


    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Method[] methods;
        boolean isCallbackImpl = false;

        // bean 的代理处理
        if (AopUtils.isAopProxy(bean)) {
            methods = AopUtils.getTargetClass(bean).getDeclaredMethods();
            for (Class<?> anInterface : AopUtils.getTargetClass(bean).getInterfaces()) {
                if (IMsgCallbackHandler.class.equals(anInterface)) {
                    isCallbackImpl = true;
                    break;
                }
            }
        } else {
            methods = bean.getClass().getDeclaredMethods();
            for (Class<?> anInterface : bean.getClass().getInterfaces()) {
                if (IMsgCallbackHandler.class.equals(anInterface)) {
                    isCallbackImpl = true;
                    break;
                }
            }
        }

        // 队列初始化
        for (Method method : methods) {
            if (method.isAnnotationPresent(MsgListener.class)) {
                MsgListener msgListener = method.getAnnotation(MsgListener.class);
                // 获取指定的队列名
                String queueName = msgListener.value();
                if (StringUtils.isEmpty(queueName)) {
                    throw new NoQueueNameSpecifiedException(method.getName() + "方法未指定一个队列名");
                }
                // 获取形参类型列表
                Class<?>[] paramTypes = method.getParameterTypes();
                MessageHandler messageHandler = new MessageHandler(paramTypes);
                for (int i = 0; i < paramTypes.length; i++) {
                    if (paramTypes[i] == Message.class) {
                        // 获取Message形参及位置
                        String genericClazzName = com.yyy.common.util.ReflectUtil.getMsgGenericClazzName(method, i);
                        messageHandler.setMethod(method);
                        messageHandler.setTarget(bean);
                        messageHandler.setGenericClazzName(genericClazzName);
                        messageHandler.setQueueName(queueName);
                        messageHandler.setMsgIndex(i);
                    }
                }
                /**
                 * 添加到容器
                 */
                append(queueName, messageHandler);
            }
        }


        // 消息回调器初始化
        if (isCallbackImpl) {
            CallbackHandler handler = bean.getClass().getDeclaredAnnotation(CallbackHandler.class);
            if (ObjectUtil.isNotNull(handler) && StringUtils.isNotEmpty(handler.name())) {
                callbackHandlerMap.put(handler.name(),(IMsgCallbackHandler) bean);
            }
        }

        return bean;
    }

    private void append(String queueName, MessageHandler messageHandler) {
        ArrayList<MessageHandler> handlers = msgHandlerMap.get(queueName);
        if (handlers == null) {
            handlers = new ArrayList<>();
        }
        handlers.add(messageHandler);
        msgHandlerMap.put(queueName, handlers);
        queueNameList.add(queueName);
        mqMapping.put(queueName, new QueueWrapper());
    }
}

